package asteroids.model.programs.statement;

import java.util.ArrayList;
import java.util.Map;

import be.kuleuven.cs.som.annotate.*;
import asteroids.model.programs.expression.*;
import asteroids.model.programs.type.*;

public class While extends S {

	private E condition;
	private S body;
	private boolean inBody;
	private boolean hasBeenInitialised;
	
	public While(int line, int column, E condition, S body) {
		super(line, column);
		this.condition = condition;
		this.body = body;
		this.hasBeenInitialised = false;
	}

	@Basic
	public E getCondition(){
		return condition;
	}
	
	@Basic
	public S getBody() {
		return body;
	}
	
	private boolean isConditionTrue(){
		if(getCondition().getValue() instanceof Bool)
			return ((Bool) getCondition().getValue()).getValue();
		return false;
	}
	
	private void setActualClause(){
		inBody = isConditionTrue();
		body.setExecuted(false);
		hasBeenInitialised = true;
	}
	
	//does nothing: while has no actual execution, if just gives back his body
	@Override
	public void execute(T type){
	}
	
	@Override
	public S getNextStatement(){
		if(hasBeenExecuted()){
			setActualClause();
			if(inBody){
				setExecuted(false);
				return getNextStatement();
			}
		}
		else if(hasSteps()){ //we are in the body, and the body is not empty
			S s = body.getNextStatement();
			if(s == body.getFirstStatement()){
				setActualClause(); //check to be sure we are in the right statement
				if(inBody){
					body.setExecuted(false); //reset the first statement to non-executed
					return body.getNextStatement();
				}
			}
			else
				return s;
		}
		else if(!hasBeenInitialised){
			setActualClause();
		}
		return null;
	}
	
	@Override
	public boolean hasSteps() {
		if(isConditionTrue())
			return body.hasSteps();
		return false;
	}	
	
	@Override
	public boolean hasBeenExecuted(){
		if(!hasBeenInitialised)
			setActualClause();
		if(inBody)
			return body.hasBeenExecuted();
		return true;
	}	
	
	@Override
	public void setExecuted(boolean b){
		body.setExecuted(b);
		super.setExecuted(b);
	}

	@Override
	public S getFirstStatement(){
		if(isConditionTrue())
			return body.getFirstStatement();
		else
			return null;
	}
	
	@Override
	public S getLastStatement(){
		return this; //if condition is true: the loop continues, so there is no last statement
					// if condition is not true: there is no (last) statement
					// to avoid nullPointers, we don't return null
	}
	
	@Override
	public ArrayList<E> getExpressions(){
		ArrayList<E> list = new ArrayList<E>();
		list.add(condition);
		return list;
	}
	
	@Override
	public ArrayList<VariableExpression> getVariableExpressions(){
		ArrayList<VariableExpression> list = getCondition().getVariables();
		list.addAll(getBody().getVariableExpressions());
		return list;
	}
	
	public String typecheck(Map<String, T> globals){
		String bTypeCheck = getBody().typecheck(globals);
		String cTypeCheck = getCondition().typecheck();
		if(!(bTypeCheck + cTypeCheck).equals(""))
			return bTypeCheck + cTypeCheck;
		T type = getCondition().getValue();
		if(type == null || type.getClass() != Bool.class)
			return "Typecast error at line " + getLine() + ", column " + getColumn() + ".\n";
		return "";
	}
	
	public boolean hasAction(){
		return body.hasAction();
	}
}
